﻿using GameLibrary;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Rescue_of_the_Dwarvenzombies.Scene;
using Rescue_of_the_Dwarvenzombies.SceneLayers;
using Rescue_of_the_Dwarvenzombies.SceneObjects;
using Rescue_of_the_Dwarvenzombies.SceneObjects.GameObjects;

namespace Rescue_of_the_Dwarvenzombies.Rendering.RenderStates
{
    /// <summary>
    /// PlayingRenderState provides the draw method thats activated when the RenderManager draws 
    /// during the PlayingState.
    /// </summary>
    /// <remarks></remarks>
    internal class PlayingRenderState : RenderState
    {
        /// <summary>
        /// 
        /// </summary>
        private float _animationTimer = 199999f;

        /// <summary>
        /// Initializes a new instance of the <see cref="PlayingRenderState"/> class.
        /// </summary>
        /// <param name="game">The game.</param>
        /// <param name="renderStateManager">The render state manager.</param>
        /// <remarks></remarks>
        public PlayingRenderState(Game game, RenderStateManager renderStateManager)
            : base(game, renderStateManager)
        {
            SpriteFont = Game.Content.Load<SpriteFont>("Fonts/ScoreFont");
        }

        /// <summary>
        /// Updates the specified game time.
        /// </summary>
        /// <param name="gameTime">The game time.</param>
        /// <remarks></remarks>
        public override void Update(GameTime gameTime)
        {
            //Add elapsed time to the animation timer
            _animationTimer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;

            //If not enough time has passed or the game is paused, return
            if (_animationTimer < Constant.ANIMATION_TIME)
                return;

            ISceneManager sceneManager = (ISceneManager)Game.Services.GetService(typeof(ISceneManager));

            ISceneLayer playerLayer = sceneManager.GetRootLayer().Child;

            //Loop through all scene objects, starting from players, in all layers until obstacles
            for (ISceneLayer layer = playerLayer; layer.LayerName != Constant.Layer.Obstacle; layer = layer.Child)
            {
                foreach (ISceneObject sceneObject in layer.GetSceneObjects())
                {
                    //Update the animations
                    AnimationManager.CheckSourceRectangle(sceneObject);
                }
            }

            //Reset the animation timer
            _animationTimer = 0;
        }

        /// <summary>
        /// Draws this instance.
        /// </summary>
        /// <remarks></remarks>
        public override void Draw()
        {
            Game.GraphicsDevice.Clear(Color.CornflowerBlue);

            ISceneManager sceneManager = (ISceneManager)Game.Services.GetService(typeof(ISceneManager));
            IRenderManager renderManager = (IRenderManager)Game.Services.GetService(typeof(IRenderManager));

            //Using the bounding box of the camera for enclosing testing
            ICamera camera = (ICamera)Game.Services.GetService(typeof(ICamera));
            //New SpriteBatch to support ground tiling
            SpriteBatch.Begin(SpriteSortMode.Deferred, null, SamplerState.LinearWrap, null, null, null, camera.GetViewMatrix());
            ISceneLayer rootLayer = sceneManager.GetRootLayer();

            //Loop through all layers
            for (ISceneLayer layer = rootLayer; layer != null; layer = layer.Child)
            {
                //Loop through all scene objects in the layer
                foreach (ISceneObject sceneObject in layer.GetSceneObjects())
                {
                    //If the scene object is within the camera view (or if the layer is the root layer), draw it
                    if (camera.BoundingBox.Intersects(sceneObject.BoundingBox) || layer.LayerName == Constant.Layer.Root)
                    {
                        SpriteBatch.Draw(renderManager.TextureDictionary[sceneObject.TextureName],
                                         sceneObject.Position, sceneObject.SourceRectangle, sceneObject.TintColor,
                                         0,
                                         Vector2.Zero,
                                         1,
                                         SpriteEffects.None, 
                                         layer.Depth);
#if DEBUG
                        RenderBoundingBox(sceneObject.BoundingBox, SpriteBatch, Game.GraphicsDevice);
#endif
                    }
                }
            }

            //Draw-call for GUI objects
            foreach (GameObject gui in sceneManager.CurrentState.GUIObjects())
            {
                if (gui.TextureName != null)
                    SpriteBatch.Draw(renderManager.TextureDictionary[gui.TextureName], gui.Position, gui.SourceRectangle,
                                  gui.TintColor);
                else
                    SpriteBatch.DrawString(SpriteFont, gui.ToString(), gui.Position, gui.TintColor);
            }

            SpriteBatch.End();
        }

        /// <summary>
        /// USED WHEN DEBUGGING
        /// Renders the bounding box for debugging purposes.
        /// </summary>
        /// <param name="boundingBox">The bounding box to be drawn</param>
        /// <param name="spriteBatch">For drawing the bounding box</param>
        /// <param name="graphicsDevice">The graphics device to use when rendering.</param>
        /// <remarks></remarks>
        public void RenderBoundingBox(BoundingBox boundingBox, SpriteBatch spriteBatch, GraphicsDevice graphicsDevice)
        {
            //Create a white texture
            Texture2D texture = new Texture2D(graphicsDevice, 1, 1, false, SurfaceFormat.Color);
            texture.SetData(new[] { Color.White });

            //Get corners from the bounding box
            Vector2 topLeft = new Vector2(boundingBox.Min.X, boundingBox.Min.Y);
            Vector2 topRight = new Vector2(boundingBox.Max.X, boundingBox.Min.Y);
            Vector2 bottomLeft = new Vector2(boundingBox.Min.X, boundingBox.Max.Y);

            //Measure the distane (width, height)
            float width = Vector2.Distance(topLeft, topRight);
            float height = Vector2.Distance(topLeft, bottomLeft);

            //Draw the four lines
            spriteBatch.Draw(texture, topLeft, null, Color.White, 0, Vector2.Zero, new Vector2(width, 2f), SpriteEffects.None, 0);
            spriteBatch.Draw(texture, topLeft, null, Color.White, MathHelper.ToRadians(90), Vector2.Zero, new Vector2(height, 2f), SpriteEffects.None, 0);
            spriteBatch.Draw(texture, topRight, null, Color.White, MathHelper.ToRadians(90), Vector2.Zero, new Vector2(height, 2f), SpriteEffects.None, 0);
            spriteBatch.Draw(texture, bottomLeft, null, Color.White, 0, Vector2.Zero, new Vector2(width, 2f), SpriteEffects.None, 0);
        }
    }
}